package stv5.managers;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import stv5.Episode;
import stv5.Request;
import stv5.Series;
import stv5.SeriesList;
import stv5.abstracted.AbstractManager;
import stv5.abstracted.EpisodeManager;

public class TversityManager implements EpisodeManager {
	private String broadIp;
	private int tvPort;
	private HashMap<Integer, ArrayList<Episode>> episodes;
	private String[] localFolders;
	

	public TversityManager(String broadcastingIp, int tvport) {
		this.broadIp = broadcastingIp;
		this.tvPort = tvport;
	}
	
	@Override
	public void appendSettingsList(HashMap<String, String> list) {
		list.put("Refresh TVersity Library", "../save/?page=tv_refresh");		
		list.put("Restart TVersity Sharing", "../save/?page=tv_restart");
		list.put("Clear TVersity Cache", "../save/?page=tv_clear");
	}
	
	@Override
	public List<Episode> getEpisodes(int seriesId) {
		return episodes.get(seriesId);
	}
	
	@Override
	public String getLocalForEpisode(Series s, Episode e) {
		String path = null, link;
		File theFile;
		
		try {
			link = URLDecoder.decode( e.getLink(), "UTF-8" );
		} catch (UnsupportedEncodingException e1) {
			e1.printStackTrace();
			return null;
		}
		link = link.substring( link.lastIndexOf('/') );
		link = link.substring(0, link.length()-4); // get rid of extra .mpg
				
		path = s.getLocalPath()+link;			
		theFile = new File(path);
		if (theFile.exists()) 
			return theFile.getAbsolutePath();
			
		return null;
	}
	
	@Override
	public void handleRequest(StringBuffer buff, Request request) {
		if ("tv_refresh".equals( request.getVar("page") )) {
			handleSimpleTversityAction(buff, "/mediasource/refreshall");
		}
		else if ("tv_restart".equals( request.getVar("page") )) {
			handleSimpleTversityAction(buff, "/general/servicerestart");
		}
		else if ("tv_clear".equals( request.getVar("page")) ) {
			handleSimpleTversityAction(buff, "/general/cachedeleteall");		
		}
	}
	
	@Override
	public void manageSeries(Series s) {
		ArrayList<Episode> list = new ArrayList<Episode>();
		
		try {
            Document doc = loadTversityPage("/medialib/browse?id="+
					s.getGuid()+"&format=rss&count=0");

            NodeList nodes = doc.getElementsByTagName("item");
            String name, link;
            for (int i=0; i<nodes.getLength(); i++) {
            	Node item = nodes.item(i);
            	// right now, tversity publishes the "title" in the first child,
            	//  and the "link" in the last child. Hopefully this will stay
            	//  the same!
            	name = item.getFirstChild().getTextContent();
            	//guid = item.getLastChild().getPreviousSibling().getTextContent();
            	link = item.getLastChild().getTextContent();
            	
            	// only add if it's not a folder!
            	if (link.indexOf("medialib/browse") == -1)
            		list.add(new Episode(name,link));
            }
            Collections.sort(list);
            episodes.put(s.getId(), list);          
            
		} catch (IOException e) {
			e.printStackTrace();	
			System.exit(1);
		} catch (SAXException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	@Override
	public void reload() {
		episodes = new HashMap<Integer, ArrayList<Episode>>();
		
		System.out.print(" - Loading local folders");
		localFolders = null;
		try {
            Document doc = loadTversityPage("/mediasource/fetchlist?type=folder");

            NodeList nodes = doc.getElementsByTagName("result");
            localFolders = new String[nodes.getLength()];
            
            for (int i=0; i<nodes.getLength(); i++) {
            	Node item = nodes.item(i);
            	localFolders[i] = item.getAttributes()
            		.getNamedItem("url").getNodeValue();
            	System.out.print(".");
            }
            
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		} catch (SAXException e) {
			e.printStackTrace();
			System.exit(1);
		}			
		System.out.println(" Done ("+localFolders.length+")");		
	}
	
	@Override
	public int size() {
		return episodes.size();
	}
	
	/**
	 * Spider through all folders, starting at root
	 * 
	 * @return
	 */
	@Override
	public SeriesList spiderFolders() {
		return spiderFolders("Folders");
	}
	
	/**
	 * Spider through all folders, starting at the given folder name
	 * 
	 * @return
	 */
	@Override
	public SeriesList spiderFolders(String folderName) {
		SeriesList ret = new SeriesList();
		doSpiderFolders(			
				"/medialib/browse?pid=0&title="+folderName+"&format=rss&count=0",
				"0",
				folderName,
				ret);

		return ret;
	}	

	/**
	 * Search through TVersity, looking for folders containing media, and
	 * 	return a hashmap with the foldername->guid
	 * Start the search at guid "baseGuid"
	 * 
	 * @param baseGuid
	 * @return
	 */
	private void doSpiderFolders(String baseGuid, String folderName, SeriesList list) {
		doSpiderFolders("/medialib/browse?id="+baseGuid+"&format=rss&count=0", 
				baseGuid, folderName, list);
	}
	
	/**
	 * Spiders from a given url
	 * 
	 * @param url
	 * @param currentName
	 * @param list
	 */
	private void doSpiderFolders(String url, String currentGuid, String currentName, SeriesList list) {
		int numVideo = 0;
        try {        	
			Document doc = loadTversityPage(url);
            NodeList nodes = doc.getElementsByTagName("item");
            for (int i=0; i<nodes.getLength(); i++) {
            	Node item = nodes.item(i);
            	// right now, tversity publishes the "title" in the first child,
            	//  and the "link" in the last child. Hopefully this will stay
            	//  the same!
            	String name = item.getFirstChild().getTextContent();
            	String guid = item.getLastChild().getPreviousSibling().getTextContent();
            	String link = item.getLastChild().getTextContent();

            	// act on folders
            	if (link.indexOf("medialib/browse") > -1) {	            	            	
	            	doSpiderFolders(guid, currentName + File.separator + name, list);
	            	         	
            	} else numVideo++; // I should really save the video here...
            }					
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		        
        if (numVideo > 0) {
        	// save this page!
			String[] tmp = currentName.split( File.separator );
			Series news = new Series( 
					AbstractManager.capitalizeWords(tmp[ tmp.length-1 ]), currentGuid);
			news.setLocalPath(currentName);
			list.add( news );
        }
		
		return;
	}		
	

	/**
	 * Handle a simple TVersity action such as restarting the DB or
	 *  clearing the cache. This uses the default TVersity-provided
	 *  message for both success and failure for convenience.
	 *  
	 * @param buff The StringBuffer on which to append the output
	 * @param url The url of the action to run
	 */
	private void handleSimpleTversityAction(StringBuffer buff, String url) {
		try {
			Document doc = loadTversityPage(url);
			Element el = doc.getDocumentElement();
			if (!"success".equals(el.getAttribute("status")) )
				buff.append("Error: " 
						+ el.getAttribute("message"));
			else
				buff.append("Success: " + el.getAttribute("message"));
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}		
	}	

	/**
	 * @param url, ex: "/medialib/browse"
	 * @return
	 * @throws SAXException
	 * @throws IOException
	 */
	private Document loadTversityPage(String url) throws SAXException, IOException {
		if (url.charAt(0) != '/')
			url = "/" + url;
		
		DOMParser parser = new DOMParser();
		parser.parse("http://"+broadIp+":"+tvPort+url);
		Document doc = parser.getDocument();
		return doc;
	}
}
